#include "file.h"
#include "image.h"
#include <fstream>
#include <iostream>
#include <string>

using namespace std;

File::File(const char* name)
{
	this->name = name;
}

void File::read(PBO* pbo)
{
	ifstream handle;
	handle.open(name);
	string version;
	handle >> version;
	handle.close();

	if(version == "P1" || version == "P2" || version == "P3")
	{
		return readAsciiFile(pbo);
	}
	else
	{
		return readBinaryFile(pbo);
	}

}

void File::save(PBO* pbo)
{
	if(pbo->fileType == ASCII)
	{
		saveAsciiFile(pbo);
	}
	else
	{
		saveBinaryFile(pbo);
	}
}

void File::readAsciiFile(PBO* pbo)
{
	ifstream handle;
	handle.open(name);
	if(!handle.is_open()) return;
	string version;
	handle >> version;
	pbo->setVersion(version);
	handle >> pbo->width;
	handle >> pbo->height;

	if(pbo->colorRange != MONO) handle >> pbo->max_value;

	int arraySize = pbo->width * pbo->height * pbo->getBytesPerPixel();
	unsigned char* data = new unsigned char[arraySize];

	int tmp;
	if(pbo->isGrayOrMono())
	{
		int factor = 255 / pbo->max_value;
		for(int i=0; i < arraySize / 3; i++)
		{
			handle >> tmp;
			if(pbo->colorRange == MONO) tmp = 1-tmp;
			for(int j=0;j<3;j++) data[i*3 +j] = tmp * factor;
		}
	}
	else
	{
		for(int i=0; i < arraySize; i++)
		{
			handle >> tmp;
			data[i] = tmp;
		}
	}

	pbo->changeTexture(data);
	handle.close();
	//if(pbo->colorRange == MONO) reverseMonoColor(image);
}

void File::readBinaryFile(PBO* pbo)
{
	ifstream handle(name, ios::binary);
	if(!handle.is_open()) return;
	string version;
	getline(handle, version);
	pbo->setVersion(version);
	pbo->width =  getlineInt(handle);
	pbo->height = getlineInt(handle);
	if(pbo->colorRange != MONO) pbo->max_value = getlineInt(handle);

	int arraySize = pbo->width * pbo->height * pbo->getBytesPerPixel();


	unsigned char* data = new unsigned char[arraySize];
	if(pbo->isGrayOrMono())
	{
		unsigned char* tmp = new unsigned char[arraySize/3];
		handle.read((char*)tmp, (pbo->getSize() / 3) * sizeof(char));
		int factor = 255 / pbo->max_value;
		for(int i = 0;i< arraySize / 3 ;i++)
		{
			if(pbo->colorRange == MONO) tmp[i] = 1 - tmp[i];
			for(int j=0;j<3;j++) data[i*3 +j] = tmp[i] * factor;
		}
	}
	else
	{
		handle.read((char*)data, pbo->getSize() * sizeof(char));
	}

	pbo->changeTexture(data);

	handle.close();
}


void File::saveAsciiFile(PBO* pbo)
{
	ofstream handle(name);
	if(!handle.is_open()) return;
	handle << pbo->getVersion() << " ";
	handle << pbo->width << " ";
	handle << pbo->height << " ";
	if(pbo->colorRange != MONO) handle << pbo->max_value << " ";
	int arraySize = pbo->width * pbo->height * pbo->getBytesPerPixel();

	unsigned char* tmp_data = pbo->getTexture();

	if(pbo->colorRange == RGB) for(int i=0; i < arraySize; i++) handle << (int)tmp_data[i] << " ";
	else if(pbo->colorRange == GRAY || pbo->colorRange == MONO) 
	{
		pbo->toGray(255, true);
		for(int i=0; i < arraySize / 3; i++) handle << (int)tmp_data[i*3] << " ";
	}

	handle.close();
}

void File::saveBinaryFile(PBO* pbo)
{
	ofstream handle(name,ios::out | ios::binary);
	if(!handle.is_open()) return;
	handle << pbo->getVersion() << "\n";
	handle << pbo->width<< "\n";
	handle << pbo->height<< "\n";
	if(pbo->colorRange != MONO) handle << pbo->max_value<< "\n";
	int arraySize = pbo->width * pbo->height * pbo->getBytesPerPixel();

	unsigned char* tmp_data = pbo->getTexture();

	if(pbo->colorRange == RGB) handle.write((char*)pbo->getTexture(), pbo->getSize() * sizeof(char));
	else if(pbo->colorRange == GRAY) 
	{
		pbo->toGray(255, true);
		unsigned char* tmp = new unsigned char[arraySize/3];
		for(int i=0; i < arraySize / 3; i++) tmp[i] = (int)tmp_data[i*3];
		handle.write((char*)tmp, pbo->getSize()/3 * sizeof(char));
	}
	else if(pbo->colorRange == MONO) 
	{
		pbo->toGray(2, true);
		bool* tmp = new bool[arraySize/3];
		for(int i=0; i < arraySize / 3; i++) tmp[i] = ((int)tmp_data[i*3] > 127)? 0 : 1;
		handle.write((char*)tmp, pbo->getSize()/3 * sizeof(char));
	}


	

	handle.close();
}

int File::getlineInt(ifstream &file)
{
	string str;
	getline(file, str); 
	return atoi(str.c_str());
}

void File::reverseMonoColor(Image* image)
{
	int size = image->width * image->height;
	unsigned char * new_data = new unsigned char[size];
	for(int i=0; i < size;i++)
	{
		if(image->data[i] == 1)new_data[i] = 0;
		else new_data[i] = 1;
	}

	delete[] image->data;
	image->data = new_data;
}